package com.team.service.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.team.service.annotation.CacheOperation;
import com.team.service.annotation.CacheSync;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 缓存同步切面
 * 实现Redis与MySQL的同步策略
 */
@Aspect
@Component
@Slf4j
public class CacheSyncAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    private final ExpressionParser parser = new SpelExpressionParser();
    private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    /**
     * 环绕通知，处理缓存同步逻辑
     */
    @Around("@annotation(cacheSync)")
    public Object around(ProceedingJoinPoint joinPoint, CacheSync cacheSync) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        // 解析缓存键
        String cacheKey = resolveKey(joinPoint, method, cacheSync);
        log.debug("CacheSync - 操作类型: {}, 缓存键: {}", cacheSync.operation(), cacheKey);
        
        // 根据操作类型执行不同的缓存策略
        if (cacheSync.operation() == CacheOperation.READ) {
            return handleReadOperation(joinPoint, cacheKey, cacheSync);
        } else {
            return handleWriteOperation(joinPoint, cacheKey, cacheSync);
        }
    }

    /**
     * 处理读操作
     * 优先读取Redis，若缓存失效则查询MySQL并回填缓存
     */
    private Object handleReadOperation(ProceedingJoinPoint joinPoint, String cacheKey, CacheSync cacheSync) throws Throwable {
        // 尝试从Redis获取数据
        String cachedValue = redisTemplate.opsForValue().get(cacheKey);
        
        if (cachedValue != null) {
            log.debug("CacheSync - 读操作命中缓存: {}", cacheKey);
            // 缓存命中，尝试将缓存值转换为方法返回类型
            try {
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                Class<?> returnType = signature.getReturnType();
                return objectMapper.readValue(cachedValue, returnType);
            } catch (Exception e) {
                log.warn("CacheSync - 缓存值反序列化失败，将从数据库获取: {}", e.getMessage());
                // 如果反序列化失败，继续执行方法从数据库获取
            }
        }
        
        // 缓存未命中或反序列化失败，执行原方法从数据库获取
        log.debug("CacheSync - 读操作未命中缓存，从数据库获取: {}", cacheKey);
        Object result = joinPoint.proceed();
        
        // 将结果存入Redis
        if (result != null) {
            try {
                String jsonValue = objectMapper.writeValueAsString(result);
                redisTemplate.opsForValue().set(
                        cacheKey, 
                        jsonValue, 
                        cacheSync.expireSeconds(), 
                        TimeUnit.SECONDS
                );
                log.debug("CacheSync - 读操作回填缓存: {}", cacheKey);
            } catch (JsonProcessingException e) {
                log.error("CacheSync - 结果序列化失败: {}", e.getMessage());
            }
        }
        
        return result;
    }

    /**
     * 处理写操作
     * 先更新MySQL，然后通过MQ异步更新Redis
     */
    private Object handleWriteOperation(ProceedingJoinPoint joinPoint, String cacheKey, CacheSync cacheSync) throws Throwable {
        try {
            // 先执行原方法更新MySQL
            Object result = joinPoint.proceed();
            
            // 如果缓存键中包含#result，则需要重新解析缓存键
            if (cacheSync.key().contains("#result")) {
                // 重新解析缓存键，使用方法返回值
                StringBuilder keyBuilder = new StringBuilder();
                
                // 添加前缀
                if (!cacheSync.keyPrefix().isEmpty()) {
                    keyBuilder.append(cacheSync.keyPrefix()).append(":");
                }
                
                // 使用方法返回值作为键
                keyBuilder.append(result);
                cacheKey = keyBuilder.toString();
                log.info("CacheSync - 使用返回值重新解析缓存键: {}", cacheKey);
            }
            
            // 构建MQ消息
            Map<String, Object> message = new HashMap<>();
            message.put("cacheKey", cacheKey);
            message.put("expireSeconds", cacheSync.expireSeconds());
            
            if (result != null) {
                try {
                    // 对于写操作，我们需要查询最新的数据作为缓存值
                    Object latestData = null;
                    
                    // 如果result是数字类型（如ID），则需要查询完整数据
                    if (result instanceof Number) {
                        // 这里应该调用相应的查询方法获取最新数据
                        // 由于无法直接调用，我们将ID放入消息中，由消费者处理
                        message.put("entityId", result);
                        message.put("requireQuery", true);
                        message.put("value", result); // 临时放入ID值
                    } else {
                        // 如果result已经是完整数据，直接使用
                        message.put("value", result);
                    }
                    
                    // 发送MQ消息，由消费者更新Redis
                    String jsonMessage = objectMapper.writeValueAsString(message);
                    log.info("CacheSync - 准备发送MQ消息，exchange: cache.sync.exchange, routingKey: {}, message: {}", cacheSync.topic(), jsonMessage);
                    rabbitTemplate.convertAndSend("cache.sync.exchange", cacheSync.topic(), jsonMessage);
                    log.info("CacheSync - 写操作已发送MQ消息: {}, routingKey: {}", cacheKey, cacheSync.topic());
                } catch (JsonProcessingException e) {
                    log.error("CacheSync - 消息序列化失败: {}", e.getMessage(), e);
                } catch (Exception e) {
                    log.error("CacheSync - 发送MQ消息失败: {}", e.getMessage(), e);
                }
            } else {
                // 如果结果为null，发送删除缓存的消息
                message.put("operation", "delete");
                String jsonMessage = objectMapper.writeValueAsString(message);
                log.info("CacheSync - 准备发送删除缓存MQ消息，exchange: cache.sync.exchange, routingKey: {}, message: {}", cacheSync.topic(), jsonMessage);
                rabbitTemplate.convertAndSend("cache.sync.exchange", cacheSync.topic(), jsonMessage);
                log.info("CacheSync - 写操作发送删除缓存消息: {}, routingKey: {}", cacheKey, cacheSync.topic());
            }
            
            return result;
        } catch (Throwable e) {
            // 方法执行异常
            log.error("CacheSync - 方法执行异常: {}", e.getMessage(), e);
            if (cacheSync.clearOnError()) {
                // 如果配置了异常时清除缓存，则删除对应的缓存
                redisTemplate.delete(cacheKey);
                log.debug("CacheSync - 方法执行异常，清除缓存: {}", cacheKey);
            }
            throw e;
        }
    }

    /**
     * 解析缓存键
     */
    private String resolveKey(ProceedingJoinPoint joinPoint, Method method, CacheSync cacheSync) {
        StringBuilder keyBuilder = new StringBuilder();
        
        // 添加前缀
        if (!cacheSync.keyPrefix().isEmpty()) {
            keyBuilder.append(cacheSync.keyPrefix()).append(":");
        }
        
        // 解析SpEL表达式
        String spelKey = cacheSync.key();
        if (spelKey.contains("#")) {
            // 获取方法参数名
            String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
            if (parameterNames != null) {
                // 创建表达式上下文
                EvaluationContext context = new StandardEvaluationContext();
                Object[] args = joinPoint.getArgs();
                
                // 将参数名和参数值放入上下文
                for (int i = 0; i < parameterNames.length; i++) {
                    context.setVariable(parameterNames[i], args[i]);
                }
                
                // 解析表达式
                Expression expression = parser.parseExpression(spelKey);
                Object value = expression.getValue(context);
                keyBuilder.append(value);
            }
        } else {
            // 不包含SpEL表达式，直接使用key值
            keyBuilder.append(spelKey);
        }
        
        return keyBuilder.toString();
    }
} 